/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.analyzer;

import org.apache.commons.lang3.StringUtils;
import org.jfab.core.collection.ImportMap;
import org.jfab.core.collection.MethodBodyList;
import org.jfab.core.collection.ParameterNameList;
import org.jfab.core.factory.CGTypeFactory;
import org.jfab.core.model.CGType;
import org.jfab.core.model.Modifier;

/**
 * Provides method analysis utilities.
 */
final class MethodAnalysisUtilities
{
    /** Line separator. */
    private static final String LINE_SEPARATOR = System
            .getProperty("line.separator");

    /** Type factory. */
    private final CGTypeFactory _typeFactory;

    /**
     * Construct this object with the given parameter.
     * 
     * @param typeFactory Type factory.
     */
    public MethodAnalysisUtilities(final CGTypeFactory typeFactory)
    {
        _typeFactory = typeFactory;
    }

    /**
     * @param line Line.
     * 
     * @return method body lines from the given parameter.
     */
    public MethodBodyList parseMethodBodyLines(final String line)
    {
        MethodBodyList answer = null;

        final String key0 = "{";
        final int blockIndex0 = line.indexOf(key0);
        final String key1 = "}";
        int blockIndex1 = line.indexOf(key1);

        if (blockIndex0 >= 0)
        {
            // Locate the matching close brace.
            int indentLevel = 1;

            while ((blockIndex0 < blockIndex1) && (indentLevel > 0))
            {
                final String text = line.substring(blockIndex0 + key0.length(),
                        blockIndex1);

                final int matches0 = StringUtils.countMatches(text, key0);
                final int matches1 = StringUtils.countMatches(text, key1);
                indentLevel = matches0 - matches1;

                if (indentLevel > 0)
                {
                    blockIndex1 = line.indexOf(key1, blockIndex1 + 1);
                }
            }

            if (blockIndex1 >= 0)
            {
                if ((blockIndex0 >= 0) && (blockIndex0 < blockIndex1))
                {
                    String text = line.substring(blockIndex0 + key0.length(),
                            blockIndex1);

                    if (StringUtils.isNotEmpty(text))
                    {
                        text = text.trim();

                        if (StringUtils.isNotEmpty(text))
                        {
                            final String[] lines = text.split(LINE_SEPARATOR);
                            answer = new MethodBodyList();

                            for (final String methodBodyLine : lines)
                            {
                                answer.add(methodBodyLine);
                            }
                        }
                    }
                }
            }
        }

        return answer;
    }

    /**
     * @param sourceCode Source code.
     * 
     * @return method parameter names.
     */
    public ParameterNameList parseParameterNames(final String sourceCode)
    {
        ParameterNameList answer = null;

        String text = StringUtils.substringBetween(sourceCode, "(", ")");

        if (StringUtils.isNotEmpty(text))
        {
            text = text.trim();
            text = text.replaceAll("\n", "");

            if (StringUtils.isNotEmpty(text))
            {
                answer = new ParameterNameList();

                final String[] items = text.split(",");

                for (String item : items)
                {
                    item = item.trim();
                    answer.add(StringUtils.substringAfterLast(item, " "));
                }
            }
        }

        return answer;
    }

    /**
     * @param packageName Package name.
     * @param importMap Import map.
     * @param sourceCode Source code.
     * @param preMethod Pre-method source code.
     * 
     * @return return type.
     */
    public CGType parseReturnType(final String packageName,
            final ImportMap importMap, final String sourceCode,
            final String preMethod)
    {
        CGType answer = null;

        String codeFragment = preMethod;
        String text = StringUtils.substringAfterLast(codeFragment, ";");

        if (StringUtils.isNotEmpty(text))
        {
            codeFragment = text;
        }

        codeFragment = StringUtils.substringAfterLast(codeFragment, "*/");

        if (codeFragment.endsWith("_"))
        {
            codeFragment = codeFragment.substring(0, codeFragment.length() - 1);
        }

        codeFragment = codeFragment.trim();

        int index0 = 0;
        int index1 = codeFragment.indexOf(' ');
        boolean isModifierFound = false;

        while ((index0 >= 0) && (index1 >= 0))
        {
            text = codeFragment.substring(index0, index1);

            final Modifier modifier = Modifier.valueOfName(text);

            if (text.startsWith("@") || (modifier != null))
            {
                index0 = index1 + 1;
                index1 = codeFragment.indexOf(' ', index0);

                if (index1 < 0)
                {
                    index1 = codeFragment.length();
                }

                isModifierFound = true;
            }
            else
            {
                if (isModifierFound)
                {
                    codeFragment = codeFragment.substring(index0);
                    index0 = -1;
                    index1 = -1;
                }
                else
                {
                    index0 = index1 + 1;
                    index1 = codeFragment.indexOf(' ', index0);
                }
            }
        }

        answer = getTypeFactory().createType(packageName, importMap,
                codeFragment);

        return answer;
    }

    /**
     * @return the typeFactory
     */
    private CGTypeFactory getTypeFactory()
    {
        return _typeFactory;
    }
}
